//
// Copyright 2019 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "usdMaya/instancerShapeAdapterWithSceneAssembly.h"

#include <mayaUsd/render/pxrUsdMayaGL/debugCodes.h>
#include <mayaUsd/utils/util.h>

#include "usdMaya/referenceAssembly.h"

#include <maya/MFnDependencyNode.h>
#include <maya/MFnMatrixData.h>

PXR_NAMESPACE_OPEN_SCOPE

UsdMayaGL_InstancerShapeAdapterWithSceneAssembly::UsdMayaGL_InstancerShapeAdapterWithSceneAssembly(
        bool isViewport2) :
    UsdMayaGL_InstancerShapeAdapter(isViewport2)
{
    TF_DEBUG(PXRUSDMAYAGL_SHAPE_ADAPTER_LIFECYCLE).Msg(
        "Constructing UsdMayaGL_InstancerShapeAdapterWithSceneAssembly: %p\n",
        this);
}

/* virtual */
UsdMayaGL_InstancerShapeAdapterWithSceneAssembly::~UsdMayaGL_InstancerShapeAdapterWithSceneAssembly()
{
    TF_DEBUG(PXRUSDMAYAGL_SHAPE_ADAPTER_LIFECYCLE).Msg(
        "Destructing UsdMayaGL_InstancerShapeAdapterWithSceneAssembly: %p\n",
        this);
}

/* virtual */
void UsdMayaGL_InstancerShapeAdapterWithSceneAssembly::SyncInstancerPerPrototypePostHook(
    const MPlug&              hierarchyPlug,
    UsdPrim&                  prototypePrim,
    std::vector<std::string>& layerIdsToMute
)
{
    UsdReferences prototypeRefs = prototypePrim.GetReferences();
    prototypeRefs.ClearReferences();

    // Collect data about what prototype this is.
    MPlug source = UsdMayaUtil::GetConnected(hierarchyPlug);
    if (source.isNull()) {
        return;
    }

    MStatus status;
    MFnDependencyNode sourceNode(source.node(), &status);
    if (!status) {
        return;
    }

    // If this is a non-full-representation USD reference assembly, add a
    // reference. Otherwise, leave the prim empty.
    if (sourceNode.typeId() != UsdMayaReferenceAssembly::typeId) {
        return;
    }

    UsdMayaReferenceAssembly* usdRefAssem =
        dynamic_cast<UsdMayaReferenceAssembly*>(
            sourceNode.userNode());
    if (!usdRefAssem) {
        return;
    }

    if (usdRefAssem->getActive() ==
        UsdMayaRepresentationFull::_assemblyType) {
        return;
    }

    UsdPrim prim = usdRefAssem->usdPrim();
    if (!prim) {
        return;
    }

    // Add main reference data.
    const std::string& layerId =
        prim.GetStage()->GetRootLayer()->GetIdentifier();
    const SdfPath primPath = prim.GetPath();
    prototypeRefs.AddReference(SdfReference(layerId, primPath));

    // Reference session data.
    // We also mute any sublayers of the session layer, because those
    // correspond to assembly edits generated by UsdMayaReferenceAssembly,
    // and UsdMayaReferenceAssembly won't give us the assembly edits
    // consistently between different representations.
    // (Most session layers won't have sublayers; they only show up when
    // there's assembly edits in Collapsed/Expanded representations.)
    // XXX Handle assembly edits on instancer prototypes?
    if (SdfLayerHandle sessionLayer = prim.GetStage()->GetSessionLayer()) {
        if (sessionLayer->GetPrimAtPath(primPath)) {
            prototypeRefs.AddReference(
                SdfReference(sessionLayer->GetIdentifier(), primPath),
                UsdListPositionFrontOfPrependList);
            const SdfSubLayerProxy subLayers =
                sessionLayer->GetSubLayerPaths();
            layerIdsToMute.insert(
                layerIdsToMute.end(),
                subLayers.begin(),
                subLayers.end());
        }
    }

    // Also handles instancerTranslate.
    // These are all in "physical", not "logical" indices.
    auto holder = UsdMayaUtil::GetPlugDataHandle(hierarchyPlug);
    MMatrix mMat = MFnMatrixData(holder->GetDataHandle().data()).matrix();
    GfMatrix4d gfMat(mMat.matrix);

    MPlug translatePlug = sourceNode.findPlug("translate", &status);
    if (status) {
        // OK if we didn't find plug, assume instancerTranslate is zero.
        GfVec3d tr(translatePlug.child(0).asDouble(),
                   translatePlug.child(1).asDouble(),
                   translatePlug.child(2).asDouble());
        gfMat = gfMat * GfMatrix4d().SetTranslate(-tr);
    }

    UsdGeomXformable xformable(prototypePrim);
    xformable.MakeMatrixXform().Set(gfMat);
}

PXR_NAMESPACE_CLOSE_SCOPE
